/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       creature_stack.cpp

	$Header: /game/creature_stack.cpp $

	$NoKeywords: $

 ************************************************************************/
#include "precompiled.h"
#include "creature_stack.h"

#include <assert.h>

#include "abstract_grail_data_source.h"
#include "adv_actor_action.h"
#include "adv_object_type.h"
#include "adventure_ai.h"
#include "ai_value.h"
#include "artifact.h"
#include "artifact_effect_type.h"
#include "artifact_slot.h"
#include "artifact_type.h"
#include "combat_sounds.h"
#include "creature_array.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "default_artifacts.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "get_artifact_damage_modifier.h"
#include "hero.h"
#include "minimum_maximum.h"
#include "missile_type.h"
#include "skill.h"
#include "spell.h"
#include "spell_properties.h"
#include "streambuf_operators.h"
#include "tactics_bonus.h"
#include "terrain.h"
#include "town_type.h"

// -----------------------------------------------------------------------
// get terrain that corresponds to a "mastery" stat
// -----------------------------------------------------------------------
namespace
{
	struct t_terrain_stat_pair
	{
		t_terrain_type  terrain;
		t_stat_type		stat;
	};

	static t_terrain_stat_pair k_terrain_stats[] =
	{
		{ k_terrain_grass,	k_stat_mastery_grass },
		{ k_terrain_rough,	k_stat_mastery_rough },
		{ k_terrain_swamp,	k_stat_mastery_swamp },
		{ k_terrain_lava,	k_stat_mastery_lava },
		{ k_terrain_snow,	k_stat_mastery_snow },
		{ k_terrain_sand,	k_stat_mastery_sand },
		{ k_terrain_dirt,	k_stat_mastery_dirt }
	};
}

// -----------------------------------------------------------------------
// get terrain that corresponds to a "mastery" stat
// -----------------------------------------------------------------------
t_terrain_type  get_terrain_type( t_stat_type stat_type )
{
	static t_terrain_type k_terrains[k_stat_count];
	static bool			  initialized = false;

	if (!initialized)
	{
		int i;
		
		for (i = 0; i < k_stat_count; ++i)
			k_terrains[i] = k_terrain_count;

		for (i = 0; i < ELEMENTS_OF( k_terrain_stats ); ++i )
			k_terrains[k_terrain_stats[i].stat] = k_terrain_stats[i].terrain;
		initialized = true;
	}
	return k_terrains[stat_type];
}


// -----------------------------------------------------------------------
// get terrain that corresponds to a "mastery" stat
// -----------------------------------------------------------------------
t_stat_type		get_stat_type( t_terrain_type terrain )
{
	static t_stat_type	k_stats[k_terrain_count];
	static bool			initialized = false;

	if (!initialized)
	{
		int i;
		
		for (i = 0; i < k_stat_count; ++i)
			k_stats[i] = k_stat_count;

		for (i = 0; i < ELEMENTS_OF( k_terrain_stats ); ++i )
			k_stats[k_terrain_stats[i].terrain] = k_terrain_stats[i].stat;
		initialized = true;
	}
	return k_stats[terrain];
}

// -----------------------------------------------------------------------
// abstract creature functions
// -----------------------------------------------------------------------
t_has_defense::~t_has_defense()
{
}

t_abstract_creature::~t_abstract_creature()
{
}

// -----------------------------------------------------------------------------
// get range effect
// -----------------------------------------------------------------------------
int const k_half_damage_range = 40;

int t_abstract_creature::get_range_effect( int distance, bool obscured ) const
{
	int result;

	if (has_ability( k_ability_long_range ))
		result = 1;
	else if (has_ability( k_ability_short_range ))
	{
		if (distance >= k_half_damage_range)
			result = 4;
		else if (distance >= k_half_damage_range / 2)
			result = 2;
		else
			result = 1;
	} else if (distance >= k_half_damage_range)
		result = 2;
	else
		result = 1;
	if (obscured && !has_ability( k_ability_siege_machine ))
		result <<= 1;
	return result;
}

// -----------------------------------------------------------------------
// determine overall modifier when attacking a target
// -----------------------------------------------------------------------
float t_abstract_creature::get_damage_modifier( t_has_defense const&	defender,
											    bool					ranged,
												float					offense ) const
{
	float defense	= defender.get_defense_basic( ranged );
	float reduction = get_defense_reduction( ranged );
	float result;

	if (reduction > defense )
	{
		if (defense > 1.0f)
			defense = 1.0f;
	}
	else
	{
		offense = offense * reduction;
	}
	defense = defense * defender.get_defense_bonus( ranged );
	result = offense / defense;
	if (result < 0.05f)
		result = 0.05f;
	if (result > 20.0f)
		result = 20.0f;
	return result;
}

// -----------------------------------------------------------------------
// determine if creature can cast spells
// -----------------------------------------------------------------------
bool t_abstract_creature::can_cast_spells() const
{
	if (get_spell_points() > 0)
		return true;

	if (get_hero() == 0)
		return false;

	// future: consider placing a test here for any knowledge of spells.
	t_artifact_slot			 slot;

	for (slot = t_artifact_slot(0); slot < k_artifact_slot_hero_count; enum_incr( slot ))
		if (get_artifact( slot ).has_effect( k_artifact_effect_spell_charges ))
			return true;

	int i;

	i = get_backpack_count();
	while (i--)
	{
		if ( get_backpack_slot(i).is_potion() )
			return true;
	}
	return false;
}

// -----------------------------------------------------------------------
// determine if a terrain is a native terrain
// -----------------------------------------------------------------------
bool t_abstract_creature::is_native_terrain( t_terrain_type terrain ) const
{
	return false;
}

t_creature_stack::t_creature_stack()
{
	m_raw_adventure_movement = 0.0f;
	memset( m_bonus, 0, sizeof( m_bonus ));
}

t_creature_stack::~t_creature_stack()
{
}

void t_creature_stack::process_new_day( t_player* owner, bool grow, t_creature_array* army,
									    int day )
{
	int i;
	t_timed_bonus* bonus;

	i = m_timed_bonus_array.size(); 
	while (i--)
	{
		bonus = &m_timed_bonus_array[i];
		if (--bonus->days_left == 0)
		{
			m_bonus[bonus->stat] -= bonus->amount;
			m_timed_bonus_array.erase( m_timed_bonus_array.begin() + i );
		}
	}
	m_raw_adventure_movement = get_raw_adventure_movement();
}

t_creature_type t_creature_stack::get_creature_type() const
{
	return k_creature_none;
}

void t_creature_stack::get_income( int day, t_material_array& income ) const
{
}

// ------------------------------------------------------------
// return the amount of movement this creature should add to an army
// as a whole.  For most creatures, this is zero normally, and 
// normal boat movement on boats.
// ------------------------------------------------------------
int t_creature_stack::get_army_move_bonus( bool in_boat ) const
{
	if (in_boat)
		return k_boat_movement;
	return 0;
}

float t_creature_stack::get_army_move_multiplier( bool in_boat ) const
{
	return 1.0f;
}

void	t_creature_stack::read_postplacement( t_adventure_map& map )
{
}

// ------------------------------------------------------------
// get adventure movement without any modifiers for hero skills,
// artifacts, or native terrain, but with modifiers for adventure
// objects.
// ------------------------------------------------------------
int t_creature_stack::get_raw_adventure_movement() const
{
	int result = get_combat_movement() * 11 / 25 + 1200;

	if (result > 3000)
		result = 3000;
	result += m_bonus[k_stat_movement];

	return result;
}


// ---------------------------------------------------------------------
// given certain information, convert raw movement to displayed 
// movement.
// ---------------------------------------------------------------------
float t_creature_stack::get_adventure_movement_modifier( float	army_move_bonus,
														 float	army_move_multiplier,
														 float	fastest_raw_move ) const
{
	float raw_move = get_raw_adventure_movement();

	if (get_hero() != 0 && raw_move < fastest_raw_move )
		army_move_multiplier *= fastest_raw_move / raw_move;
	
	return army_move_multiplier + army_move_bonus / raw_move;
}


namespace
{
	struct t_terrain_cost
	{
		t_terrain_type terrain;
		int            cost;
	};
};

int get_terrain_cost( t_terrain_type terrain )
{
	static int  cost[k_terrain_count];
	static bool initialized = false;

	if (!initialized)
	{
		initialized = true;

		static t_terrain_cost const k_costs[] =
		{
			{ k_terrain_rough,			125 },
			{ k_terrain_swamp,			200 },
			{ k_terrain_lava,			125 },
			{ k_terrain_snow,			175 },
			{ k_terrain_sand,			150 },
			{ k_terrain_water_river,	150 },
			{ k_terrain_ice_river,		150 },
			{ k_terrain_magic_death,	125 },
			{ k_terrain_magic_chaos,	125 },
			{ k_terrain_might,			125 },
		};

		int i;

		for (i = 0; i < k_terrain_count; i++)
			cost[i] = 100;
		for (i = 0; i < ELEMENTS_OF( k_costs ); i++)
			cost[k_costs[i].terrain] = k_costs[i].cost;
	}
	return cost[terrain];
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_army_move_cost( t_terrain_type terrain ) const
{
	return get_terrain_cost( terrain );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_move_cost( t_terrain_type terrain ) const
{
	return get_terrain_cost( terrain );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::add( t_artifact const& artifact, bool equip )
{
	return false;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::add_to_backpack( t_artifact const& artifact, int slot )
{
	return false;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
static t_artifact const k_dummy_artifact;
t_artifact const& t_creature_stack::get_artifact( t_artifact_slot slot ) const
{
	return k_dummy_artifact;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_backpack_count() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
t_artifact const& t_creature_stack::get_backpack_slot( int slot ) const
{
	return k_dummy_artifact;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
t_artifact t_creature_stack::remove_backpack( int slot )
{
	return t_artifact();
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// return false for all abilities.
// ------------------------------------------------------------
bool t_creature_stack::has_ability( t_creature_ability ability ) const
{
	return false;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::can_add( t_creature_type creature ) const
{
	return true;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::can_add( t_creature_stack const& arg ) const
{
	return true;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
t_creature const* t_creature_stack::get_const_creature() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::can_cast( t_spell spell ) const
{
	return false;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_bonus( t_stat_type stat ) const
{
	return m_bonus[stat];
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_damage_high() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_damage_low() const
{
	return 0;
}

float t_creature_stack::get_defense( bool ranged, t_creature_array const& army ) const
{
	return get_defense_basic( ranged ) * get_defense_bonus( ranged );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
float t_creature_stack::get_defense_basic( bool ranged ) const
{
	return 1.0f;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
float t_creature_stack::get_defense_bonus( bool ranged ) const
{
	return 1.0f;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
float t_creature_stack::get_defense_reduction( bool ranged ) const
{
	return 1.0f;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
float t_creature_stack::get_offense( bool ranged ) const
{
	return 1.0f;
}

float t_creature_stack::get_offense( bool ranged, t_creature_array const& army ) const
{
	return get_offense( ranged );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::ai_get_total_offense( bool ranged ) const
{
	return (get_damage_high() + get_damage_low()) 
			* get_offense( ranged ) 
			* get_number();
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::ai_get_total_offense() const
{
	int damage = (get_damage_high() + get_damage_low());

	float offense_ranged = get_offense( true );
	float offense_melee = get_offense( false );

	return damage 
			* maximum(offense_ranged, offense_melee)
			* get_number();
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::ai_get_total_defense( bool ranged ) const
{
	return get_hit_points() 
			* get_defense_basic( ranged ) 
			* get_defense_bonus( ranged ) 
			* get_number();
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::ai_get_total_defense() const
{
	return (ai_get_total_defense( false ) + ai_get_total_defense( true )) / 2;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
float t_creature_stack::ai_get_average_defense() const
{
	return 
		(get_defense_basic( false ) * get_defense_bonus( false ) 
		+ get_defense_basic( true ) * get_defense_bonus( true )) 
		/ 2;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
t_hero const* t_creature_stack::get_const_hero() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_hit_points() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_combat_movement() const
{
	return 0;
}

t_missile_type t_creature_stack::get_missile_type() const
{
	return k_missile_none;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
std::string t_creature_stack::get_name( bool include_article, int ) const
{
	return "";
}

std::string t_creature_stack::get_army_name() const
{
	return get_name();
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_number() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::knows_spell( t_spell spell ) const
{
	return false;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::read( std::streambuf& stream )
{
	return true;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::write( std::streambuf& stream ) const
{
	return true;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_creature_stack::read_from_map( std::streambuf& stream )
{
	return true;
}


// ------------------------------------------------------------
// Skill levels. No skills for non-heroes
// ------------------------------------------------------------
t_skill_mastery t_creature_stack::get_skill( t_skill_type skill ) const
{
	return k_mastery_none;
}

int	t_creature_stack::get_tactics_defense_bonus( bool sea_battle ) const
{
	return 0;
}

int	t_creature_stack::get_tactics_offense_bonus( bool sea_battle ) const
{
	return 0;
}

int t_creature_stack::get_tactics_speed_bonus( bool sea_battle ) const
{
	return 0;
}

int t_creature_stack::get_leadership_luck_bonus() const
{
	return 0;
}

int	t_creature_stack::get_leadership_morale_bonus() const
{
	return 0;
}

int	t_creature_stack::get_tactics_move_bonus( bool sea_battle ) const
{
	return 0;
}



// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_spell_cost( t_spell spell ) const
{
	return ::get_spell_cost( spell );
}

// ------------------------------------------------------------
// Power level for spell
// ------------------------------------------------------------
int t_creature_stack::get_spell_power( t_spell spell, t_abstract_grail_data_source const & grail_data, int number ) const
{
	int bonus = 100 + grail_data.get_grail_spell_bonus( get_spell_alignment( spell ) );

	return ::get_basic_spell_power( spell, 0, bonus );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
t_bitmap_layer_ptr t_creature_stack::get_portrait( int height, bool force_awake_portrait ) const
{
	return t_bitmap_layer_ptr();
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_shots() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_speed( t_creature_array const& army ) const
{
	return get_speed();
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_speed() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_maximum_spell_points() const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_spell_points() const
{
	return 0;
}

void t_creature_stack::learn_spells( t_town const* town )
{
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
void t_creature_stack::remove( int amount )
{
	assert( false );
}

t_town_type t_creature_stack::get_alignment() const
{
	return k_town_none;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_experience_value( int number ) const
{
	return 0;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_luck() const
{
	int result = m_bonus[k_stat_luck];
	if ( is_spell_active( k_spell_luck_boost ) )
		result += 5;
	return result;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_luck( t_creature_array const& army ) const
{
	t_hero const* hero;
	int		bonus;
	int		highest_bonus = 0;
	int		i;

	for (i = 0; i < t_creature_array::k_size; ++i)
	{
		hero = army[i].get_hero();
		if (hero == 0)
			continue;
		bonus = get_luck_bonus( *hero );
		if (highest_bonus < bonus)
			highest_bonus = bonus;
	}
	return get_luck() + highest_bonus;
}

int t_creature_stack::get_luck_bonus( t_hero const& hero ) const
{
	return get_artifact_luck( hero );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::get_morale( t_creature_array const& army ) const
{
	t_hero const* hero;
	int		bonus;
	int		highest_bonus = 0;
	int		i;

	for (i = 0; i < t_creature_array::k_size; ++i)
	{
		hero = army[i].get_hero();
		if (hero == 0)
			continue;
		bonus = get_morale_bonus( *hero );
		if (highest_bonus < bonus)
			highest_bonus = bonus;
	}
	return army.get_morale( get_alignment() ) + get_morale() + highest_bonus;
}

int t_creature_stack::get_morale_bonus( t_hero const& hero ) const
{
	return get_artifact_morale( hero );
}

std::string t_creature_stack::get_right_click_text() const
{
	return "";
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int	t_creature_stack::get_morale() const
{
	int result = m_bonus[k_stat_morale];
	if ( is_spell_active( k_spell_morale_boost ) )
		result += 5;
	return result;
}

// --------------------------------------------------------
// information about a hero
// add a temporary bonus (until next battle)
// --------------------------------------------------------
void t_creature_stack::add_bonus( t_stat_type stat, int amount, 
							      t_qualified_adv_object_type const& type )
{
	t_temporary_bonus bonus;

	bonus.amount = amount;
	bonus.stat = stat;
	bonus.object_type = type;
	m_bonus_array.push_back( bonus );
	m_bonus[stat] += amount;
}

// --------------------------------------------------------
// add a temporary bonus
// with a type of k_adv_object_nothing
// --------------------------------------------------------
void t_creature_stack::add_temp_bonus( t_stat_type stat, int amount )
{
	t_qualified_adv_object_type type = k_adv_object_nothing;
	add_bonus(stat, amount, type);
}

// --------------------------------------------------------
// get bonus time remaining
// return 0 if none
// assumes that multiple bonuses all have the same time
// --------------------------------------------------------
int t_creature_stack::get_bonus_time( t_qualified_adv_object_type const& type ) const
{
	int i;

	i = m_timed_bonus_array.size();
	while (i--)
		if (m_timed_bonus_array[i].object_type == type)
			return m_timed_bonus_array[i].days_left;
	return 0;
}

// --------------------------------------------------------
// information about a hero
// add a temporary bonus (until next battle)
// --------------------------------------------------------
bool t_creature_stack::add_timed_bonus( t_stat_type stat, int amount, 
							            t_qualified_adv_object_type const& type,
										int days )
{
	t_timed_bonus bonus;

	// check if creature already has bonus
	int i;
	for (i = 0; i < m_timed_bonus_array.size(); ++i)
		if (m_timed_bonus_array[i].object_type == type
			&& m_timed_bonus_array[i].stat == stat)
			break;
	if (i < m_timed_bonus_array.size())
	{
		if (m_timed_bonus_array[i].days_left >= days)
			return false;
		m_timed_bonus_array[i].days_left = days;
		return true;
	}
	bonus.amount = amount;
	bonus.stat = stat;
	bonus.object_type = type;
	bonus.days_left = days;
	m_timed_bonus_array.push_back( bonus );
	m_bonus[stat] += amount;
	return true;
}

// --------------------------------------------------------
// information about a hero
// clear temporary bonuses
// --------------------------------------------------------
void t_creature_stack::clear_temporary_bonuses()
{
	int i;

	for (i = 0; i < m_bonus_array.size(); i++)
		m_bonus[m_bonus_array[i].stat] -= m_bonus_array[i].amount;
	m_bonus_array.clear();
}

// --------------------------------------------------------
// information about a hero
// check if hero has visited an object
// --------------------------------------------------------
bool t_creature_stack::has_temporary_bonus( t_qualified_adv_object_type const& type ) const
{
	int i;

	for (i = 0; i < m_bonus_array.size(); i++)
		if (m_bonus_array[i].object_type == type)
			return true;
	return false;
}


// --------------------------------------------------------
// --------------------------------------------------------
bool t_creature_stack::has_timed_bonus( t_qualified_adv_object_type const& type ) const
{
	int i;

	for (i = 0; i < m_timed_bonus_array.size(); i++)
		if (m_timed_bonus_array[i].object_type == type)
			return true;
	return false;
}

// --------------------------------------------------------
// abstract creature
// --------------------------------------------------------
int t_creature_stack::get_wounds() const
{
	return 0;
}

// --------------------------------------------------------
// abstract creature
// --------------------------------------------------------
bool t_creature_stack::is_active() const
{
	return false;
}

// --------------------------------------------------------
// abstract creature
// --------------------------------------------------------
bool t_creature_stack::is_spell_active( t_spell spell ) const
{
	assert( spell >= 0 && spell < k_spell_count );
	return false;
}

// --------------------------------------------------------
// abstract creature
// --------------------------------------------------------
bool t_creature_stack::is_dead() const
{
	return false;
}

// --------------------------------------------------------
// information about a hero
// check if hero has visited an object
// --------------------------------------------------------
bool t_creature_stack::is_undead() const
{
	return false;
}


// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int t_creature_stack::compute_scouting_range() const
{
	return 0;
}

// --------------------------------------------------------
// empty creature stack
// get combat model
// --------------------------------------------------------
t_combat_actor_model_ptr t_creature_stack::get_combat_model( double scale ) const
{
	return t_combat_actor_model_ptr();
}

// --------------------------------------------------------
// default movement sound for a creature stack, which is
// --------------------------------------------------------
t_sound_ptr t_creature_stack::get_sound( t_adv_actor_action_id action )
{
	return get_combat_sound( *this, action );
}

// --------------------------------------------------------
// empty creature stack
// --------------------------------------------------------
bool t_creature_stack::preplacement( t_adventure_map& map, int pass, t_player* owner )
{
	m_raw_adventure_movement = get_raw_adventure_movement();
	return false;
}

// --------------------------------------------------------
// empty creature stack
// --------------------------------------------------------
int t_creature_stack::get_stoning_chance() const
{
	return 0;
}

// --------------------------------------------------------
// --------------------------------------------------------
float t_creature_stack::ai_value() const
{
	float bonus_modifier = 1.0f;
	if (m_bonus[k_stat_luck])
	{
		bonus_modifier += ai_bonus_for_luck(m_bonus[k_stat_luck]);
	}
	if (m_bonus[k_stat_morale]) 
	{
		bonus_modifier += ai_bonus_for_morale(m_bonus[k_stat_morale]);
	}
	return get_experience_value(get_number()) * k_ai_creature_value_modifier * bonus_modifier;
}

// --------------------------------------------------------
// --------------------------------------------------------
float t_creature_stack::ai_value( t_creature_array const* army ) const
{
	float bonus_modifier = 1.0f;
	if (m_bonus[k_stat_luck])
	{
		bonus_modifier += ai_bonus_for_luck(m_bonus[k_stat_luck]);
	}
	if (m_bonus[k_stat_morale]) 
	{
		bonus_modifier += ai_bonus_for_morale( 
							m_bonus[k_stat_morale] 
							+ army->get_ai()->get_cache().get_alignment_modifier( army, get_alignment()) );
	}
	return get_experience_value(get_number()) * k_ai_creature_value_modifier * bonus_modifier;

}

// --------------------------------------------------------
// --------------------------------------------------------
t_creature_stack::t_clone_ptr t_creature_stack::clone() const
{
	t_creature_stack* ptr = new t_creature_stack( *this );
	return ptr;
}

// --------------------------------------------------------
// --------------------------------------------------------
t_creature_stack::t_bonus_array const& t_creature_stack::get_bonus_array() const
{
	return m_bonus_array;
}

// --------------------------------------------------------
// --------------------------------------------------------
bool t_creature_stack::set_spell( t_spell spell, bool active )
{
	assert( spell >= 0 && spell < k_spell_count );
	return false;
}

// --------------------------------------------------------
// --------------------------------------------------------
int t_creature_stack::get_magic_resistance( t_player const* owner, t_spell spell ) const
{
	return 0;
}

// --------------------------------------------------------
// --------------------------------------------------------
void t_creature_stack::on_begin_turn( t_player* owner, t_creature_array* army )
{
}

// --------------------------------------------------------
// --------------------------------------------------------
void t_creature_stack::on_end_turn( t_player* owner, t_creature_array* army )
{
}

// --------------------------------------------------------
// Redistribute artifacts from this creature to (other) 
// creatures in army. If the artifacts can't go anywhere,
// put them in leftovers.
// --------------------------------------------------------
void t_creature_stack::redistribute_artifacts( t_creature_array & army, t_artifact_list & leftovers )
{
}

void t_creature_stack::copy_bonuses( t_creature_stack const& source )
{
	int i;

	for (i = 0; i < k_stat_count; i++)
	{
		m_bonus[i] = source.m_bonus[i];
	}
	m_bonus_array = source.m_bonus_array;
	m_timed_bonus_array = source.m_timed_bonus_array;
}

// --------------------------------------------------------
// --------------------------------------------------------
t_skill_mastery t_creature_stack::get_anti_stealth_level() const
{
	return k_mastery_none;
}

t_skill_mastery t_creature_stack::get_stealth_level() const
{
	return k_mastery_none;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_stack_with_backpack::add( t_artifact const& artifact, bool equip )
{
	m_backpack.insert( m_backpack.begin(), artifact );
	return true;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_stack_with_backpack::add_to_backpack( t_artifact const& artifact, int slot )
{
	if (slot >= m_backpack.size() || slot < 0)
		m_backpack.insert( m_backpack.begin(), artifact );
	else
		m_backpack.insert( m_backpack.begin() + slot, artifact );
	return true;
}


// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
t_artifact t_stack_with_backpack::remove_backpack( int slot )
{
	t_artifact result;

	if (slot < m_backpack.size() && slot >= 0 )
	{
		result = m_backpack[slot];
		m_backpack.erase( m_backpack.begin() + slot );
	}
	return result;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_stack_with_backpack::is_spell_active( t_spell spell ) const
{
	assert( spell >= 0 && spell < k_spell_count );
	return m_active_spells.test( spell );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_stack_with_backpack::set_spell( t_spell spell, bool active )
{
	assert( spell >= 0 && spell < k_spell_count );
	m_active_spells.set( spell, active );
	return true;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
int const k_version = 22;

bool t_stack_with_backpack::read_header( std::streambuf& stream, int& version )
{
	int               count;
	t_temporary_bonus bonus;

	version = get<t_uint16>( stream );
	if (version > k_version )
		return false;
	if (version >= 7)
	{
		::read( stream, m_bonus, sizeof( m_bonus ));
	}
	else
	{
		memset( m_bonus, 0, sizeof( m_bonus ));
		::read( stream, m_bonus, sizeof(int) * (k_stat_spell_points + 1) );
	}
	if (version < 18)
		set_raw_adventure_movement( get<int>( stream ) );
	else
		set_raw_adventure_movement( get<float>( stream ) );
	count = get<t_uint16>( stream );
	m_bonus_array.clear();
	while (count--)
	{
		bonus.amount = get<int>( stream );
		bonus.stat = get<t_stat_type>( stream );
		::read( stream, bonus.object_type );
		m_bonus_array.push_back( bonus );
	}
	m_timed_bonus_array.clear();
	if (version >= 6)
	{
		t_timed_bonus timed_bonus;

		count = get<t_uint16>( stream );
		while (count--)
		{
			timed_bonus.amount = get<int>(stream);
			timed_bonus.stat = t_stat_type( get<t_uint8>(stream) );
			::read( stream, timed_bonus.object_type );
			timed_bonus.days_left = get<t_uint16>( stream );
			m_timed_bonus_array.push_back( timed_bonus );
		}
	}
	count = get<t_uint16>( stream );
	while (count--)
	{
		m_backpack.push_back( t_artifact() );
		if (!m_backpack.back().read( stream ))
			return false;
	}
	if ( version >= 13 && !::read( stream, m_active_spells ) )
		return false;

	return true;
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_stack_with_backpack::write_header( std::streambuf& stream ) const
{
	int                      i;
	t_temporary_bonus const* bonus;

	put<t_uint16>( stream, k_version );
	::write( stream, m_bonus, sizeof( m_bonus ));
	put<float>( stream, get_raw_adventure_movement_left() );
	put<t_uint16>( stream, m_bonus_array.size() );
	for (i = 0; i < m_bonus_array.size(); i++)
	{
		bonus = &m_bonus_array[i];
		put<int>( stream, bonus->amount );
		put<t_stat_type>( stream, bonus->stat );
		::write( stream, bonus->object_type );
	}

	t_timed_bonus const* timed_bonus;

	put<t_uint16>( stream, m_timed_bonus_array.size() );
	for (i = 0; i < m_timed_bonus_array.size(); ++i)
	{
		timed_bonus = &m_timed_bonus_array[i];
		put<int>( stream, timed_bonus->amount );
		put<t_uint8>( stream, timed_bonus->stat );
		::write( stream, timed_bonus->object_type );
		put<t_uint16>( stream, timed_bonus->days_left );
	}

	put<t_uint16>( stream, m_backpack.size() );
	for (i = 0; i < m_backpack.size(); i++)
		m_backpack[i].write( stream );

	return ::write( stream, m_active_spells );
}

// ------------------------------------------------------------
// object with either a hero, a set of creatures, or nothing.
// ------------------------------------------------------------
bool t_stack_with_backpack::read_from_map( std::streambuf& stream )
{
	int artifact_count = get< t_uint16 >( stream );

	t_artifact_list new_backpack;
	new_backpack.reserve( artifact_count );

	while ( artifact_count > 0 )
	{
		new_backpack.push_back( t_artifact() );
		if ( !::read_from_map( stream, new_backpack.back() ) )
			return false;

		--artifact_count;
	}

	if ( !t_creature_stack::read_from_map( stream ) )
		return false;

	m_backpack.swap( new_backpack );

	return true;
}

// --------------------------------------------------------
// check if creature has a bonus for a terrain
// --------------------------------------------------------
bool t_stack_with_backpack::has_terrain_mastery( t_terrain_type terrain ) const
{
	t_stat_type stat = get_stat_type( terrain );

	if (stat == k_stat_count)
		return false;
	return m_bonus[stat] > 0;
}

// --------------------------------------------------------
// --------------------------------------------------------
void t_stack_with_backpack::on_begin_turn( t_player* owner, t_creature_array* army )
{
	t_creature_stack::on_begin_turn( owner, army );

	if ( is_spell_active( k_spell_mire ) )
	{
		set_raw_adventure_movement( get_raw_adventure_movement() * 0.5f );
		set_spell( k_spell_mire, false );
	}
}

// --------------------------------------------------------
// --------------------------------------------------------
void t_stack_with_backpack::on_end_turn( t_player* owner, t_creature_array* army )
{
	set_spell( k_spell_endurance, false );
	set_spell( k_spell_pathfinding, false );
	set_spell( k_spell_visions, false );
	t_creature_stack::on_end_turn( owner, army );
}

// --------------------------------------------------------
// --------------------------------------------------------
int t_stack_with_backpack::get_army_move_cost( t_terrain_type terrain ) const
{
	assert( terrain >= 0 && terrain < k_terrain_count );
	if ( is_spell_active( k_spell_pathfinding ) )
		return 100;
	return t_creature_stack::get_army_move_cost( terrain );
}

// --------------------------------------------------------
// Redistribute artifacts from this creature to (other) 
// creatures in army. If the artifacts can't go anywhere,
// put them in leftovers.
// --------------------------------------------------------
void t_stack_with_backpack::redistribute_artifacts( t_creature_array & army, t_artifact_list & leftovers )
{
	// Find somewhere to put them; prefer live heroes over dead ones and dead heroes over creatures
	t_creature_stack * target_stack = NULL;

	t_creature_stack * target_creatures = NULL;
	t_creature_stack * target_dead_hero = NULL;
	int i;

	for ( i = 0; i < t_creature_array::k_size && target_stack == NULL; i++ )
	{
		if ( &army[i] == this )
			continue;

		t_hero * hero = army[i].get_hero();
		if ( hero )
		{
			if ( hero->is_dead() )
			{
				target_dead_hero = hero;
			}
			else
			{
				target_stack = hero;
			}
		}
		else if ( army[i].get_number() > 0 )
		{
			target_creatures = &army[i];
		}
	}

	if ( target_stack == NULL )
	{
		if ( target_dead_hero == NULL )
		{
			target_stack = target_creatures;
		}
		else
		{
			target_stack = target_dead_hero;
		}
	}

	while ( get_backpack_count() > 0 )
	{
		t_artifact artifact = remove_backpack( 0 );

		assert( artifact.get_icon() != k_artifact_none );

		if ( !target_stack || !target_stack->add( artifact ) )
			leftovers.push_back( artifact );
	}
}
